Open Assets Protocol
Open Assets Protocolについて
アセットの発行主体が存在する。
ブロックチェーン上でビットコイン以外のアセットを発行・取引するために機能を定義している。
→土地 の登録簿記、商品・サービスと交換可能なポイント発行、流通の所有権の移転を利用したシェアリングエコノミーの実現
・Marker Output
トランザクションがビットコインではなくアセットを送付しているトランザクションで有ることを示すアウトプット
・Asset Quantity
取引でいくつかのアセットが取引されているのか示すアセットの量
・Asset ID
ブロックチェーン上で取引されるアセットを一意に識別するためのID
・Open Assets Address
ビットコインアドレスと1対1に対応するアセットを取引する際に使用するアドレス
・Asset Definition Protocol
アセットに関するメタ情報(名称や発行者など)とアセットを紐付けるためのプロトコル
Marker Outputについて
トランザクションがビットコインの取引化アセットの取引かを区別するために、Marker Outputと呼ばれる特殊なアウトプットが一つ含まれている。
Marker OutputはOP_RETURNの後にOpen Assets Payloadのデータをプッシュするスクリプト
OP_RETURN <Open Assets Payload>
その他→p232
アセットの発行と送付の違い
トランザクションにセットするアウトプットの位置によってタイプを判別する。
(Marker Outputより前にOutputがあるかないかで判断)
https://gyazo.com/ae2b3e23ba063e6feddedb8154e3c531
図3:発行用トランザクション
https://gyazo.com/52d8f8c3ad6ce252e58fc5055d3f5c2e
図4:送付用トランザクション
このMarker Outputにはトランザクションで扱うAssets Quantityのセットが格納されており、そのセットに定義されている
アセット量をMarker Output以外のアウトプットに上から順に適応することで、各アウトプットのAssets Quantityが決まる
Asset IDの計算
https://gyazo.com/cc35c6cfb5ddc3aaabe4177705845ad2
図1:Asset IDの算出元
Asset IDは発行トランザクションの最初のインプットが参照しているアウトプットのスクリプトから計算される
発行トランザクションだけでは運用できない。送付トランザクションもなくっちゃね!
このふたつは役割が違うんだけど、ふたつでひとつの組みあわせ。
こんな感じー
https://gyazo.com/07110b88c20ea0425d14ad107b26907d
図2:Asset IDの計算フロー
Asset IDはアセット発行トランザクションの入力(ここではInput 1が指すところ)から計算するので
Maker Outputにはアセットの量が記載されているが、Asset IDは記載されていない。
つまるところ
Asset IDをきめるには、アセットの発行トランザクションまで戻って、Asset IDを計算することになる
code:Asset IDの計算手順
~ $irb
> require 'bitcoin'
=> true
> key = Bitcoin::Key.generate
=> # <Bitcoin::Key:0x00007fb011024e00 @key=#<OpenSSL::PKey::EC:0x00007fb011024d88>, @pubkey_compressed=true>
> private_key = key.priv
=> "ab57d151ebb52511e2a84b2d8ffbc2c9d9d56dea214d5639632162eb787b32ef"
> public_key = key.pub
=> "03aa0786101a3fc29773279c647f93e3ab20800c84478f94310b0fa301fc600214"
> script = Bitcoin::Script.to_hash160_script(key.hash160)
=> "v\xA9\x14\ab\xD8\a\xC8T*\x92R\xA5\"}N\\\xA4\x01\"\xD4\x14\xF2\x88\xAC"
> hash = Bitcoin::hash160(script.bth)
=> "130940de4419300df1993569c70ba013b56d8623"
> hash = 23.to_s(16) + hash
=> "17130940de4419300df1993569c70ba013b56d8623"
> asset_id = Bitcoin.encode_base58(hash + Bitcoin.checksum(hash))
=> "AHWXa5bSL8dCfWmpAW7QX4oe2rZzXS7vQD"
発行者はこのAsset IDを生成するのに使用した秘密鍵を所持していれば、いつでも同じAsset IDのアセットを発行することができる。逆に秘密鍵を持っていない者は発行できない。また、発行トランザクションまで遡らなければならないため、SPVノード単体では扱えない。フルノードとの連携が必要になる。
各アウトプットへのAsset IDの割り当て
order-based-coloringという方法でアウトプットに適応するAsset IDを決めている
トランザクションの各アウトプットが保持しているアセットをMrker Outputに定義されているAsset Quantityの数だけトランザクションの各アウトプットに割り当てる
説明はめんどくさいので詳しくははバイブルをみてください https://gyazo.com/28b4095b501b5ac55c99a95e58e25aa0
図:3order-based-coloringの例
Open Assets Addressについて
Open Assets Protocolに対応していないウォレットでは、Open Assets Protocolによって色付けされたアウトプットも通常のビットコインとして処理されるため、そのアウトプットのアセット情報は破壊されてしまう。対応していないウォレットにアセットを送付するのを防ぐため、ビットコインアドレスとは異なるOpen Assets Addressが使用される。Open Assets Addressとビットコインアドレスは1対1でマッピングされる。
・Open Assets Addressのフォーマット
base58-encode: [ネームスペース][バージョン][ペイロード][チェックサム]
ネームスペースは0x13、バージョンはもとのビットコインアドレスのバージョンバイト、ペイロードはもとのビットコインアドレスのペイロード、チェックサムはネームスペースとバージョンとペイロードにSHA256ダブルはッシュした値の先頭4バイト。
Open Assets Addressの算出
・ビットコインアドレスを生成
2NF3V83tqhTGicDXzuGFuiq9HDbERxSF1Qw
・ビットコインアドレスをbase58デコーディングする
C4EF1A68AA9A09EA907A4ACDC3FD38A006ED9798C94A5E980Cz
code:デコーディングの確認(もとのアドレスと同じか)
> Bitcoin::encode_base58('C4EF1A68AA9A09EA907A4ACDC3FD38A006ED9798C94A5E980C')
=> "2NF3V83tqhTGicDXzuGFuiq9HDbERxSF1Qw"
・デコーディングしたものにネームスペースの13をつけてbase58でエンコードする
code:Open Assets Addressの生成
> Bitcoin::encode_base58('13C4EF1A68AA9A09EA907A4ACDC3FD38A006ED9798C94A5E980C')
=> "c7R1NNDiBAwAQUThMwtN6Nh3rZCQbwZYurB"
Asset Definition Protocolについて
Asset IDによりアセットを一意に識別することはできるが、そのアセットがどんなアセットで誰が発行者なのかはブロックチェーン上に記録されてない。アセットに関する方法や付加条件を定義したAsset Definitionをブロックチェーン外で公開し、ブロックチェーン上のアセットと関連付けるしくみがAsset Definition Protocol。
アセットの発行者はAsset Definition Pointerという文字列を使ってメタデータのファイルとアセットをブロックチェーン
上で関連づける
このAsset Definition Pointerの定義が3つあって、ブロックチェーン と関連づける方法が2つある
(ややこしい)
アセットの発行
code:設定
require 'bitcoin'
require 'openassets'
require 'pp'
require 'json'
include Bitcoin::Util
Bitcoin.network = :testnet3
oa_api = OpenAssets::Api.new({:network => 'testnet',
:provider => 'bitcoind', :cache => 'testnet.db',
:dust_limit => 600, :default_fees => 10000,
:min_confirmation => 0, :max_confirmation => 9999999,
:rpc => {:user => 'user', :password => 'password', :schema => 'http',
:port => 18332, :host => 'localhost'}})
code:発行
btc_address = "2N3PGzfcupV2xPAt3AQhRizR8BMENktau7q"
oa_address = OpenAssets.address_to_oa_address(btc_address)
code:送金
> tx = Bitcoin::Protocol::Tx.new
> prev_tx_hash = "07a7b668feaef629772096423274f0568539774848a7691dc98dacdb2552bdf7"
=> "07a7b668feaef629772096423274f0568539774848a7691dc98dacdb2552bdf7"
> prev_output_index = 2
=> 2
> tx_in = Bitcoin::Protocol::TxIn.from_hex_hash(prev_tx_hash, prev_output_index)
=> #<Bitcoin::Protocol::TxIn:0x00007f8e0668ef20 @prev_out_hash="\xF7\xBDR%\xDB\xAC\x8D\xC9\x1Di\xA7HHw9\x85V\xF0t2B\x96 w)\xF6\xAE\xFEh\xB6\xA7\a", @prev_out_index=2, @script_sig_length=0, @script_sig="", @sequence="\xFF\xFF\xFF\xFF", @script_witness=#<Bitcoin::Protocol::ScriptWitness:0x00007f8e0668ee58 @stack=[]>> > tx.add_in(tx_in)
=> [#<Bitcoin::Protocol::TxIn:0x00007f8e0668ef20 @prev_out_hash="\xF7\xBDR%\xDB\xAC\x8D\xC9\x1Di\xA7HHw9\x85V\xF0t2B\x96 w)\xF6\xAE\xFEh\xB6\xA7\a", @prev_out_index=2, @script_sig_length=0, @script_sig="", @sequence="\xFF\xFF\xFF\xFF", @script_witness=#<Bitcoin::Protocol::ScriptWitness:0x00007f8e0668ee58 @stack=[]>>]
> script = OpenAssets::Protocol::MarkerOutput.new(10, 30) > script = OpenAssets::Protocol::MarkerOutput.new(10, 30).build_script => #<Bitcoin::Script:0x00007f8e0692c200 @raw_byte_sizes=10, 0, @previous_output_script=nil, @input_script="j\bOA\x01\x00\x02\n\x1E\x00", @parse_invalid=nil, @inner_p2sh=nil, @script_codeseparator_index=nil, @raw="j\bOA\x01\x00\x02\n\x1E\x00", @chunks=106, "OA\x01\x00\x02\n\x1E\x00", @exec_stack=[], @stack_alt=[], @stack=[], @last_codeseparator_index=0, @do_exec=true> > tx.add_out(Bitcoin::Protocol::TxOut.new(0, script.to_payload))
> tx_out1 = Bitcoin::Protocol::TxOut.value_to_address(600, "2NAnNzyjoimUv5zmK2aETrs5GCoo39jnX3v")
> tx_out2 = Bitcoin::Protocol::TxOut.value_to_address(600, "2N6569NW3hYBMqbQ5EUovx2H3PCdjeWNwje")
> tx.add_out(tx_out1)
> tx.add_out(tx_out2)
> tx_in1 = Bitcoin::Protocol::TxIn.from_hex_hash("07a7b668feaef629772096423274f0568539774848a7691dc98dacdb2552bdf7", 3)
=> #<Bitcoin::Protocol::TxIn:0x00007f8e0706a788 @prev_out_hash="\xF7\xBDR%\xDB\xAC\x8D\xC9\x1Di\xA7HHw9\x85V\xF0t2B\x96 w)\xF6\xAE\xFEh\xB6\xA7\a", @prev_out_index=3, @script_sig_length=0, @script_sig="", @sequence="\xFF\xFF\xFF\xFF", @script_witness=#<Bitcoin::Protocol::ScriptWitness:0x00007f8e0706a6c0 @stack=[]>> > tx.add_in(tx_in1)
=> [#<Bitcoin::Protocol::TxIn:0x00007f8e0668ef20 @prev_out_hash="\xF7\xBDR%\xDB\xAC\x8D\xC9\x1Di\xA7HHw9\x85V\xF0t2B\x96 w)\xF6\xAE\xFEh\xB6\xA7\a", @prev_out_index=2, @script_sig_length=0, @script_sig="", @sequence="\xFF\xFF\xFF\xFF", @script_witness=#<Bitcoin::Protocol::ScriptWitness:0x00007f8e0668ee58 @stack=[]>>, #<Bitcoin::Protocol::TxIn:0x00007f8e0706a788 @prev_out_hash="\xF7\xBDR%\xDB\xAC\x8D\xC9\x1Di\xA7HHw9\x85V\xF0t2B\x96 w)\xF6\xAE\xFEh\xB6\xA7\a", @prev_out_index=3, @script_sig_length=0, @script_sig="", @sequence="\xFF\xFF\xFF\xFF", @script_witness=#<Bitcoin::Protocol::ScriptWitness:0x00007f8e0706a6c0 @stack=[]>>] > tx.to_payload.bth
=> "0100000002f7bd5225dbac8dc91d69a7484877398556f074324296207729f6aefe68b6a7070200000000fffffffff7bd5225dbac8dc91d69a7484877398556f074324296207729f6aefe68b6a7070300000000ffffffff0300000000000000000a6a084f410100020a1e00580200000000000017a914c05e8e53c28ba0404f2945478fd155a353c4e15187580200000000000017a9148caeeda504d75d0c8759c1fc73fa3120c836201e8700000000"
> OpenAssets::Protocol::MarkerOutput.deserialize_payload("4f410100020a1e00")
> tx_out3 = Bitcoin::Protocol::TxOut.value_to_address(995000, "2N6569NW3hYBMqbQ5EUovx2H3PCdjeWNwje")
> tx.add_out(tx_out3)
=> #<Bitcoin::Protocol::TxOut:0x00007f8e0691e060 @value=0, @pk_script_length=10, @pk_script="j\bOA\x01\x00\x02\n\x1E\x00">, #<Bitcoin::Protocol::TxOut:0x00007f8e05bb06f8 @value=600, @pk_script_length=23, @pk_script="\xA9\x14\xC0^\x8ES\xC2\x8B\xA0@O)EG\x8F\xD1U\xA3S\xC4\xE1Q\x87">, #<Bitcoin::Protocol::TxOut:0x00007f8e0708b190 @value=600, @pk_script_length=23, @pk_script="\xA9\x14\x8C\xAE\xED\xA5\x04\xD7\f\x87Y\xC1\xFCs\xFA1 \xC86 \x1E\x87">, #<Bitcoin::Protocol::TxOut:0x00007f8e0690fdd0 @value=995000, @pk_script_length=23, @pk_script="\xA9\x14\x8C\xAE\xED\xA5\x04\xD7]\f\x87Y\xC1\xFCs\xFA1 \xC86 \x1E\x87">] > tx.to_payload.bth
=> "0100000002f7bd5225dbac8dc91d69a7484877398556f074324296207729f6aefe68b6a7070200000000fffffffff7bd5225dbac8dc91d69a7484877398556f074324296207729f6aefe68b6a7070300000000ffffffff0400000000000000000a6a084f410100020a1e00580200000000000017a914c05e8e53c28ba0404f2945478fd155a353c4e15187580200000000000017a9148caeeda504d75d0c8759c1fc73fa3120c836201e87b82e0f000000000017a9148caeeda504d75d0c8759c1fc73fa3120c836201e8700000000"
>